package com.better.common.mongo.repository;

import com.better.common.mongo.model.BaseModel;
import com.better.common.mongo.model.SelelctType;
import com.better.common.mongo.model.SpringDataPageable;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import static org.springframework.data.mongodb.core.query.Criteria.where;

/**
 * Created by Administrator on 2017/11/1.
 */
public class CommonMongoRepositoryImpl<T extends BaseModel, ID extends Serializable> extends SimpleMongoRepository<T, ID> implements CommonRepository<T, ID> {
    protected final MongoOperations mongoTemplate;

    protected final MongoEntityInformation<T, ID> entityInformation;

    public CommonMongoRepositoryImpl(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
        super(metadata, mongoOperations);
        this.mongoTemplate = mongoOperations;
        this.entityInformation = metadata;
    }

    /**
     * 字符串分隔的标志符号，现在用:表示，在关联查询的时候用。
     ***/
    private final String splitMarker = "::";
    /**
     * 字符串分隔的标志符号，现在用.表示，在内嵌对象查询的时候用。
     ***/
    private final String elemMatchMarker = ".";
    /**
     * 字符串分割标识符号
     **/
    private final String splitElemMachtMarke = "\\.";

    protected Class<T> getEntityClass() {
        return entityInformation.getJavaType();
    }

    /**
     * 子类文档统计数key
     **/
    private String TOTAL = "total";
    /**
     * 获取总数记录KEY
     **/
    private String RESULT = "result";

    /**
     * 自定义查询query 分页
     *
     * @param query 查询dsl语句
     * @param p     分页对象，传入空，代表查询所有
     * @author:
     * @date :2017/11/17
     */
    @Override
    public Page<T> findPageByQuery(Query query, Pageable p) {
        long total = mongoTemplate.count(query, getEntityClass());
        List<T> list = mongoTemplate.find(query.with(p), getEntityClass());
        return new PageImpl<T>(list, p, total);
    }

    /**
     * 自定义查询query 分页
     *
     * @param query 查询dsl语句
     * @author:
     * @date :2017/11/17
     */
    @Override
    public List<T> findListByQuery(Query query) {
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 自定义  Criteria 分页查询
     *
     * @param criteria 标准查询语句
     * @param p        分页对象，传入空，代表查询所有
     * @author:
     * @date :2017/11/17
     */
    @Override
    public Page<T> findPageByCriteria(Criteria criteria, Pageable p) {
        return findPageByQuery(new Query(criteria), p);
    }
    /**
     * 自定义 Criteria 查询
     *@author:
     *@date :2018/1/8
     */
    @Override
    public List<T> findByCriteria(Criteria criteria){
        return mongoTemplate.find(new Query(criteria), getEntityClass());
    }
    /**
     * 自定义 Criteria 查询
     *@author:
     *@date :2018/1/8
     */
    @Override
    public List<T> findForField(DBObject dbObject, BasicDBObject basicDBObject){
        Query query = new BasicQuery(dbObject,basicDBObject);
        return mongoTemplate.find(query,getEntityClass());
    }
    /**
     * 更新文档
     *
     * @param id             更新主键
     * @param updateFieldMap key:需要更新的属性  value:对应的属性值
     * @author:
     * @date :2017/11/17
     */
    @Override
    public void update(ID id, Map<String, Object> updateFieldMap) {
        if (null != updateFieldMap && !updateFieldMap.isEmpty()) {
            Criteria criteria = new Criteria("_id").is(id);
            Update update = new Update();
            updateFieldMap.entrySet().forEach(entry -> update.set(entry.getKey(), entry.getValue()));
            mongoTemplate.findAndModify(new Query(criteria), update, getEntityClass());
        }
    }

    /**
     * 更新文档
     *
     * @param queryParamMap  key:查询属性  value:对应的属性值
     * @param updateFieldMap key:需要更新的属性  value:对应的属性值
     * @author:
     * @date :2017/11/17
     */
    @Override
    public void update(Map<String, Object> queryParamMap, Map<String, Object> updateFieldMap) {
        if (null != queryParamMap && !queryParamMap.isEmpty()) {
            Criteria criteria = bulidCriteria(queryParamMap);
            if (null != updateFieldMap && !updateFieldMap.isEmpty()) {
                Update update = new Update();
                updateFieldMap.entrySet().forEach(entry -> update.set(entry.getKey(), entry.getValue()));
                mongoTemplate.findAndModify(new Query(criteria), update, getEntityClass());
            }

        }
    }


    /**
     * 批量保存更新（如果不存在就查询，存在就更新）
     *
     * @author:
     * @date :2017/11/17
     */
    @Override
    public void batchSave(List<T> obj) {
        if (CollectionUtils.isNotEmpty(obj)) {
            //过滤掉集合中的空对象
            obj = obj.stream().filter(o -> Objects.nonNull(o)).collect(Collectors.toList());
            save(obj);
        }
    }

    /**
     * 根据主键IDs 批量删除
     *
     * @author:
     * @date :2017/11/17
     */
    @Override
    public void batchDelete(List<ID> pks) {
        if (CollectionUtils.isNotEmpty(pks)) {
            Query query = new Query(Criteria.where("_id").in(pks));
            mongoTemplate.findAllAndRemove(query, getEntityClass());
        }
    }

    /**
     * 分页查询列表
     *
     * @param pageIndex 当前页
     * @param pageSize  当前页面条数
     * @param sortMap   排序 key:排序字段 value:升序0或降序1
     * @return
     */
    @Override
    public Page<T> findPage(Integer pageIndex, Integer pageSize, Map<String, Integer> sortMap) {
        List<Sort.Order> orders = bulidSort(sortMap);
        SpringDataPageable pageable = null;
        if (CollectionUtils.isNotEmpty(orders)) {
            pageable = new SpringDataPageable(pageIndex, pageSize, new Sort(orders));
        } else {
            pageable = new SpringDataPageable(pageIndex, pageSize);
        }
        Page<T> page = findAll(pageable);
        return new PageImpl<T>(page.getContent(), pageable, page.getTotalElements());
    }

    /**
     * 多条件查询排序
     *
     * @param paramMap 查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @param sortMap  排序 key:排序字段 value:升序0或降序1
     * @author:
     * @date :2017/11/17
     */
    @Override
    public List<T> findListBy(Map<String, Object> paramMap, Map<String, Integer> sortMap) {
        List<Sort.Order> orders = bulidSort(sortMap);
        Query query = this.buildQuery(paramMap, orders, null);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 多条件查询
     *
     * @param paramMap 查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @author:
     * @date :2017/11/17
     */
    @Override
    public List<T> findListBy(Map<String, Object> paramMap) {
        Query query = this.buildQuery(paramMap, null, null);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 多条件查询排序
     *
     * @param paramMap        查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @param sortMap         排序 key:排序字段 value:升序0或降序1
     * @param includeParamMap 映射字段,为空，类似 select * from dual  不为空，类似 select filed1 ,filed2 ,...from dual
     * @author:
     * @date :2017/11/17
     */
    @Override
    public List<T> findListBy(Map<String, Object> paramMap, Map<String, Integer> sortMap, Map<String, Object> includeParamMap) {
        List<Sort.Order> orders = bulidSort(sortMap);
        Query query = this.buildQuery(paramMap, orders, includeParamMap);
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 多条件查询
     *
     * @param propertyName 查询字段
     * @param value        匹配值
     * @author:
     * @date :2017/11/17
     */
    @Override
    public List<T> findListBy(String propertyName, Object value) {
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(propertyName, value);
        return this.findListBy(paramMap, null);
    }

    /**
     * 多条件查询排序分页
     *
     * @param paramMap  查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @param sortMap   排序 key:排序字段 value:升序0或降序1
     * @param pageIndex 当前页
     * @param pageSize  当前页面条数
     * @author:
     * @date :2017/11/17
     */
    @Override
    public Page<T> findPageBy(Map<String, Object> paramMap, Map<String, Integer> sortMap, Integer pageIndex, Integer pageSize) {
        List<Sort.Order> orders = bulidSort(sortMap);
        Query query = this.buildQuery(paramMap, orders);
        SpringDataPageable pageable = new SpringDataPageable(pageIndex, pageSize);
        return this.findPageByQuery(query, pageable);
    }

    /**
     * 多条件查询分页
     *
     * @param paramMap  查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @param pageIndex 当前页
     * @param pageSize  当前页面条数
     * @author:
     * @date :2017/11/17
     */
    @Override
    public Page<T> findPageBy(Map<String, Object> paramMap, Integer pageIndex, Integer pageSize) {
        Query query = this.buildQuery(paramMap, null);
        SpringDataPageable pageable = new SpringDataPageable(pageIndex, pageSize);
        return this.findPageByQuery(query, pageable);
    }

    /**
     * 条件查询分页
     *
     * @param propertyName 查询字段
     * @param pageIndex    当前页
     * @param pageSize     当前页面条数
     * @author:
     * @date :2017/11/17
     */
    @Override
    public Page<T> findPageBy(String propertyName, Object value, Integer pageIndex, Integer pageSize) {
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(propertyName, value);
        Query query = this.buildQuery(paramMap, null);
        SpringDataPageable pageable = new SpringDataPageable(pageIndex, pageSize);
        return this.findPageByQuery(query, pageable);
    }

    /**
     * 按照查询添加统计记录
     *
     * @param paramMap 查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @author:
     * @date :2017/11/20
     */
    @Override
    public long countBy(Map<String, Object> paramMap) {
        Query query = this.buildCountQuery(paramMap);
        long total = mongoTemplate.count(query, getEntityClass());
        return total;
    }

    /**
     * 分页查询内嵌文档
     *
     * @author:
     * @date :2017/12/1
     */
    @Override
    public <O> List<O> findNestPage(TypedAggregation<?> aggregation, Class<?> inputType, Class<O> outputType) {
        return mongoTemplate.aggregate(aggregation, inputType, outputType).getMappedResults();
    }

    /**
     * 分页查询内嵌文档
     *
     * @author:
     * @date :2017/12/1
     */
    @Override
    public <O> Page<O> findNestPage(Map<String, Object> querParms, Map<String, String> filedAlias, String nestProperties, String groupFiled,
                                    SpringDataPageable pageable, Class<?> inputType, Class<O> outputType) {
        return findNestPage(querParms, filedAlias, null, nestProperties, groupFiled, pageable, inputType, outputType);
    }

    @Override
    public <O> Page<O> findNestPage(NestCriteria criteria, Map<String, String> filedAlias, String nestProperties, String groupFiled, SpringDataPageable pageable, Class<?> inputType, Class<O> outputType) {
        Assert.hasText(nestProperties, "nestProperties  must not be null or empty!");
        //检查下是否有嵌套属性的别名指定
        if( !filedAlias.containsValue(nestProperties)){
            throw new IllegalArgumentException("filedAlias  must  contain nestProperties");
        }
        List<AggregationOperation> commonOperations = new ArrayList<>();
        //给属性指定别名
        if (null != filedAlias && !filedAlias.isEmpty()) {
            List<org.springframework.data.mongodb.core.aggregation.Field> list = new ArrayList<>();
            List<String> fildsShow = new ArrayList<>();
            for (Map.Entry<String, String> entry : filedAlias.entrySet()) {
                if (StringUtils.isEmpty(entry.getKey())) {
                    Assert.hasText(entry.getKey(), entry.getKey() + " must not be null or empty!");
                } else if (StringUtils.isNotEmpty(entry.getKey()) && StringUtils.isNotEmpty(entry.getValue())) {//指定别名
                    list.add(Fields.field(entry.getValue(), entry.getKey()));
                } else {//显示字段默认和key值一致
                    list.add(Fields.field(entry.getKey(), entry.getKey()));
                }
            }

            int size = list.size();
            org.springframework.data.mongodb.core.aggregation.Field[] field = list.toArray(new org.springframework.data.mongodb.core.aggregation.Field[size]);
            Fields fields = Fields.from(field);
            commonOperations.add(project(fields));
        }else{
            Assert.notNull(filedAlias, "filedAlias  must not be null or empty!");
        }
        //展开子文档
        commonOperations.add(unwind(nestProperties));
        //组装查询条件
        if(criteria!=null){
            commonOperations.add(match(criteria));
        }
        //判断是否分页
        List<AggregationOperation> pageOperations = new ArrayList<>(commonOperations);
        if (pageable != null) {
            pageOperations.add(skip(pageable.getOffset()));
            pageOperations.add(limit(pageable.getPageSize()));
        }
        //查询集合
        TypedAggregation<?> pageAgg = Aggregation.newAggregation(inputType, pageOperations);
        List<O> results = this.findNestPage(pageAgg, inputType, outputType);
        //统计
        List<AggregationOperation> totalAggOperation = new ArrayList<>(commonOperations);
        totalAggOperation.add(group(groupFiled).count().as(this.TOTAL));
        TypedAggregation totalAgg = Aggregation.newAggregation(inputType, totalAggOperation);
        //判断是否进行了筛选
        long total=0L;
        if(criteria.getkey().containsKey(groupFiled)){
            total = this.findNestCout(totalAgg, String.class,true);
        }else {
            total = this.findNestCout(totalAgg, String.class,false);
        }
        return new PageImpl<O>(results, pageable, total);
    }

    @Override
    public <O> Page<O> findNestPage(NestCriteria criteria, Map<String, String> filedAlias, Map<String, Integer> sortMap, String nestProperties, String groupFiled, SpringDataPageable pageable, Class<?> inputType, Class<O> outputType) {
        Assert.hasText(nestProperties, "nestProperties  must not be null or empty!");
        //检查下是否有嵌套属性的别名指定
        if( !filedAlias.containsValue(nestProperties)){
            throw new IllegalArgumentException("filedAlias  must  contain nestProperties");
        }
        List<AggregationOperation> commonOperations = new ArrayList<>();
        //给属性指定别名
        if (null != filedAlias && !filedAlias.isEmpty()) {
            List<org.springframework.data.mongodb.core.aggregation.Field> list = new ArrayList<>();
            List<String> fildsShow = new ArrayList<>();
            for (Map.Entry<String, String> entry : filedAlias.entrySet()) {
                if (StringUtils.isEmpty(entry.getKey())) {
                    Assert.hasText(entry.getKey(), entry.getKey() + " must not be null or empty!");
                } else if (StringUtils.isNotEmpty(entry.getKey()) && StringUtils.isNotEmpty(entry.getValue())) {//指定别名
                    list.add(Fields.field(entry.getValue(), entry.getKey()));
                } else {//显示字段默认和key值一致
                    list.add(Fields.field(entry.getKey(), entry.getKey()));
                }
            }

            int size = list.size();
            org.springframework.data.mongodb.core.aggregation.Field[] field = list.toArray(new org.springframework.data.mongodb.core.aggregation.Field[size]);
            Fields fields = Fields.from(field);
            commonOperations.add(project(fields));
        }else{
            Assert.notNull(filedAlias, "filedAlias  must not be null or empty!");
        }
        //展开子文档
        commonOperations.add(unwind(nestProperties));
        //组装查询条件
        if(criteria!=null){
            commonOperations.add(match(criteria));
        }
        //排序
        if (null != sortMap && !sortMap.isEmpty()) {
            List<Sort.Order> orders = bulidSort(sortMap);
            commonOperations.add(sort(new Sort(orders)));
        }
        //判断是否分页
        List<AggregationOperation> pageOperations = new ArrayList<>(commonOperations);
        if (pageable != null) {
            pageOperations.add(skip(pageable.getOffset()));
            pageOperations.add(limit(pageable.getPageSize()));
        }
        //查询集合
        TypedAggregation<?> pageAgg = Aggregation.newAggregation(inputType, pageOperations);
        List<O> results = this.findNestPage(pageAgg, inputType, outputType);
        //统计
        List<AggregationOperation> totalAggOperation = new ArrayList<>(commonOperations);
        totalAggOperation.add(group(groupFiled).count().as(this.TOTAL));
        TypedAggregation totalAgg = Aggregation.newAggregation(inputType, totalAggOperation);
        //long total = this.findNestCout(totalAgg, String.class,true);
        //判断是否进行了筛选
        long total=0L;
        if(criteria.getkey().containsKey(groupFiled)){
            total = this.findNestCout(totalAgg, String.class,true);
        }else {
            total = this.findNestCout(totalAgg, String.class,false);
        }
        return new PageImpl<O>(results, pageable, total);
    }

    /**
     * 分页查询内嵌文档
     *
     * @author:
     * @date :2017/12/1
     */
    @Override
    public <O> Page<O> findNestPage(Map<String, Object> querParms, Map<String, String> filedAlias, Map<String, Integer> sortMap, String nestProperties, String groupFiled,
                                    SpringDataPageable pageable, Class<?> inputType, Class<O> outputType) {
        Assert.hasText(nestProperties, "nestProperties  must not be null or empty!");
        Assert.hasText(groupFiled, "groupFiled  must not be null or empty!");
        //检查下是否有嵌套属性的别名指定
        if( !filedAlias.containsValue(nestProperties)){
            throw new IllegalArgumentException("filedAlias  must  contain nestProperties");
        }
        List<AggregationOperation> commonOperations = new ArrayList<>();
        //给属性指定别名
        if (null != filedAlias && !filedAlias.isEmpty()) {
            List<org.springframework.data.mongodb.core.aggregation.Field> list = new ArrayList<>();
            List<String> fildsShow = new ArrayList<>();
            for (Map.Entry<String, String> entry : filedAlias.entrySet()) {
                if (StringUtils.isEmpty(entry.getKey())) {
                    Assert.hasText(entry.getKey(), entry.getKey() + " must not be null or empty!");
                } else if (StringUtils.isNotEmpty(entry.getKey()) && StringUtils.isNotEmpty(entry.getValue())) {//指定别名
                    list.add(Fields.field(entry.getValue(), entry.getKey()));
                } else {//显示字段默认和key值一致
                    list.add(Fields.field(entry.getKey(), entry.getKey()));
                }
            }
            int size = list.size();
            org.springframework.data.mongodb.core.aggregation.Field[] field = list.toArray(new org.springframework.data.mongodb.core.aggregation.Field[size]);
            Fields fields = Fields.from(field);
            commonOperations.add(project(fields));
        }else{
            Assert.notNull(filedAlias, "filedAlias  must not be null or empty!");
        }
        //展开子文档
        commonOperations.add(unwind(nestProperties));
        //组装查询条件
        if (null != querParms && !querParms.isEmpty()) {
            for (Map.Entry<String, Object> entry : querParms.entrySet()) {
                Optional.ofNullable(entry.getValue()).ifPresent(s -> commonOperations.add(match(where(entry.getKey()).is(entry.getValue()))));
            }
        }
        //排序
        if (null != sortMap && !sortMap.isEmpty()) {
            List<Sort.Order> orders = bulidSort(sortMap);
            commonOperations.add(sort(new Sort(orders)));
        }
        //判断是否分页
        List<AggregationOperation> pageOperations = new ArrayList<>(commonOperations);
        if (pageable != null) {
            pageOperations.add(skip(pageable.getOffset()));
            pageOperations.add(limit(pageable.getPageSize()));
        }
        //查询集合
        TypedAggregation<?> pageAgg = Aggregation.newAggregation(inputType, pageOperations);
        List<O> results = this.findNestPage(pageAgg, inputType, outputType);
        //统计
        List<AggregationOperation> totalAggOperation = new ArrayList<>(commonOperations);
        totalAggOperation.add(group(groupFiled).count().as(this.TOTAL));
        TypedAggregation totalAgg = Aggregation.newAggregation(inputType, totalAggOperation);
        //判断是否进行了筛选
        long total=0L;
        if(querParms.containsKey(groupFiled)){
            total = this.findNestCout(totalAgg, String.class,true);
        }else {
            total = this.findNestCout(totalAgg, String.class,false);
        }

        return new PageImpl<O>(results, pageable, total);
    }


    public long findNestCout(TypedAggregation<?> aggregation, Class outputType,boolean select) {
        DBObject rawResults = mongoTemplate.aggregate(aggregation, outputType).getRawResults();
        BasicDBList result = (BasicDBList) rawResults.get(this.RESULT);
        long total=0L;
        if(select){
            total = result.isEmpty() ? 0 : Long.parseLong(((DBObject) result.get(0)).get(TOTAL).toString());
        }else {
            total = result.size();
        }

        return total;
    }

    /**
     * 按条件获取对象
     *
     * @param propertyName 查询字段
     * @param value        匹配值
     * @author:
     * @date :2017/11/17
     */
    @Override
    public T findUniqueBy(String propertyName, Object value) {
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(propertyName, value);
        Query query = this.buildQuery(paramMap, null);
        return mongoTemplate.findOne(query, getEntityClass());
    }

    /**
     * 按条件获取对象
     *
     * @param paramMap 查询 key:查询字段 value:匹配值（数字和_id精确查询，字符串默认模糊查询）
     * @author:
     * @date :2017/11/17
     */
    @Override
    public T findUniqueBy(Map<String, Object> paramMap) {
        Query query = this.buildQuery(paramMap, null);
        return mongoTemplate.findOne(query, getEntityClass());
    }


    /**
     * 构建排序
     *
     * @author:
     * @date :2017/11/22
     */
    protected List<Sort.Order> bulidSort(Map<String, Integer> sortMap) {
        List<Sort.Order> orders = new ArrayList<>();
        if (sortMap != null && !sortMap.isEmpty()) {
            sortMap.entrySet().forEach(entry -> orders.add(new Sort.Order(entry.getValue() == 0 ? Sort.Direction.ASC : Sort.Direction.DESC, entry.getKey())));
        }
        return orders;
    }

    /**
     * 构建 Criteria查询语句
     *
     * @author:
     * @date :2017/11/22
     */
    protected Criteria bulidCriteria(Map<String, Object> queryParamMap) {
        List<Criteria> criteriaList = new ArrayList<>();
        for (Map.Entry<String, Object> entry : queryParamMap.entrySet()) {
            criteriaList.add(Criteria.where(entry.getKey()).is(entry.getValue()));
        }
        int size = criteriaList.size();
        Criteria[] criterias = new Criteria[size];
        for (int i = 0; i < size; i++) {
            criterias[i] = criteriaList.get(i);
        }
        Criteria criteria = new Criteria().andOperator(criterias);
        return criteria;
    }

    /**
     * 构建统计sql ,区别统计sql不需要映射字段
     * @author:
     * @date :2018/1/8
     */
    protected Query buildCountQuery(Map<String, Object> paramMap) {
        return buildQuery(paramMap, null, null,SelelctType.COUNT_SQL);
    }

    /**
     * 构建query 查询语句
     *
     * @author:
     * @date :2017/11/22
     */
    protected Query buildQuery(Map<String, Object> paramMap, List<Sort.Order> orders) {
        return buildQuery(paramMap, orders, null);
    }

    protected Query buildQuery(Map<String, Object> paramMap, List<Sort.Order> orders, Map<String, Object> includeParamMap) {
        return buildQuery(paramMap,orders,includeParamMap,SelelctType.NOMAL_SQL);
    }
    /**
     * 构建query 查询语句
     *@param type  type类型不为空，则是统计，统计无需进行字段映射
     * @author:
     * @date :2017/11/22
     */
    protected Query buildQuery(Map<String, Object> paramMap, List<Sort.Order> orders, Map<String, Object> includeParamMap,String type) {
        Query query = new Query();
        if (null != paramMap && !paramMap.isEmpty()) {
            for (String key : paramMap.keySet()) {
                Object value = paramMap.get(key);
                if (key.contains(this.splitMarker)) {// 处理关联查询的情况
                    String[] arr = key.split(this.splitMarker);
                    if (arr.length != 2) {
                        continue;
                    }
                    String p1 = arr[0];
                    String p2 = arr[1];
                    Class<T> clazz = getEntityClass();
                    Field[] fs = this.getAllFields(clazz);
                    Class<?> clazz1 = null;
                    for (Field f : fs) {
                        if (f.getName().equals(p1)) {
                            clazz1 = f.getType();
                            break;
                        }
                    }
                    Query query0 = new Query();
                    this.buildQuery(p2, value, query0);
                    query.addCriteria(Criteria.where(p1).in(mongoTemplate.find(query0, clazz1)));

                } else if (key.contains(this.elemMatchMarker)) {//内嵌文档查询
                    type=SelelctType.NEST_SQL;//解决由于内嵌文档查询，导致父类文档查询的字段未显示问题
                    String[] arr = key.split(this.splitElemMachtMarke);
                    if (arr.length < 2) {
                        continue;
                    }
                    StringBuilder includeFile = new StringBuilder();
                    for (int i = 0; i < arr.length - 1; i++) {
                        if (i == 0) {
                            includeFile.append(arr[i]);
                        } else {
                            includeFile.append(this.elemMatchMarker).append(arr[i]);
                        }
                    }
                    includeFile.append(".$");
                    //暂时支撑三层内嵌文档查询
                    if (arr.length == 2) {
                        String p1 = arr[0];
                        String p2 = arr[1];
                        query.addCriteria(Criteria.where(p1).elemMatch(Criteria.where(p2).is(value))).fields().include(includeFile.toString());
                    } else {
                        String p1 = arr[0];
                        String p2 = arr[1];
                        String p3 = arr[2];
                        query.addCriteria(Criteria.where(p1).elemMatch(Criteria.where(p2).elemMatch(Criteria.where(p3).is(value)))).fields().include(includeFile.toString());
                    }

                } else {
                    this.buildQuery(key, value, query);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(orders)) {
            Sort sort = new Sort(orders);
            query.with(sort);
        }
        //字段映射，如果为空，默认查询出所有字段
        org.springframework.data.mongodb.core.query.Field filed = query.fields();
        if (null != includeParamMap && !includeParamMap.isEmpty()) {
            includeParamMap.forEach((k, v) -> {
                filed.include(k);
            });
        } else {
            if(!SelelctType.COUNT_SQL.equals(type)){
                if(SelelctType.NEST_SQL.equals(type)){
                    //获取实体类中所有的字段
                    Class<T> clazz = getEntityClass();
                    Field[] fs = this.getAllFields(clazz);
                    for (Field f : fs) {
                        //排除嵌套查询的字段（因为在内嵌文档的时候已经指定了映射，为什么不在这里统一做，是因为，如果多于2层嵌套后，字段映射问题）
                        //同时排除序列化字段，此字段在数据库不保存，不然会导致异常
                        Type genericType = f.getGenericType();
                        if (!(genericType instanceof ParameterizedType||f.getName().equals("serialVersionUID"))) {
                            filed.include(f.getName());
                        }
                    }
                }
            }
        }
        return query;
    }

    protected void buildQuery(String key, Object value, Query query) {
        // 如果为数字
        if (key.contains("_id")) {
            query.addCriteria(Criteria.where(key).is(new ObjectId(value.toString())));
        } else {
            if (value instanceof Integer || value instanceof Float || value instanceof Double
                    || value instanceof Boolean) {
                query.addCriteria(Criteria.where(key).is(value));
            } else if (value instanceof String) {
                // 字符串 模糊查询
                // i为查询大小写不敏感
                query.addCriteria(Criteria.where(key).regex(value.toString(), "i"));
            } else if (value instanceof List) {//如何是集合类型,暂不支持，需要自己写嵌套查询
                List<Object> list = (List) value;
            /*    if(CollectionUtils.isNotEmpty(list)){
                    for(Object o:list){
                        Class clss = getgenericClazz(key);
                        System.out.println(clss.toString());
                        query.addCriteria(Criteria.where(key).elemMatch(Criteria.byExample(o)));
                    }
                }*/
            }
        }
    }

    /**
     * 获取list 总的泛型
     *
     * @author:
     * @date :2017/11/17
     */
    private Class<?> getgenericClazz(String key) {
        Field[] fields = getEntityClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        List list = new ArrayList();
        Map infoMap = null;
        for (Field f : fields) {
            if (f.getName().equals(key)) {
                f.setAccessible(true);
                Type genericType = f.getGenericType();
                if (genericType == null) continue;
                // 如果是泛型参数的类型
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                    return genericClazz;
                }
            }
        }
        return null;
    }


    /**
     * 获取属性类型(type)，属性名(name)，属性值(value)的map组成的list
     */
    private String getFiledsInfo(String key) {
        Field[] fields = getEntityClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        List list = new ArrayList();
        Map infoMap = null;
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(key)) {
                return fields[i].getType().getName();
            }
        }
        return null;
    }

    private Field[] getAllFields(Class<? super T> clazz) {
        Class<?> originClazz = clazz;
        Field[] resultField = clazz.getDeclaredFields();

        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            if (clazz == originClazz) {
                continue;
            }
            try {
                Field[] superFields = clazz.getDeclaredFields();
                resultField = (Field[]) ArrayUtils.addAll(resultField, superFields);
            } catch (Exception e) {
                System.out.println("error");
            }
        }
        return resultField;
    }
}
